#pragma once
#include "thread.hpp"
#include "lockGuard.hpp"
#include "log.hpp"

const int g_thread_num = 10;

// 本质是: 生产消费模型
template<class T>
class ThreadPool
{
public:
    pthread_mutex_t* getMutex()
    {
        return &lock;
    }

    bool isEmpty()
    {
        return task_queue_.empty();
    }

    void waitCond()
    {
        pthread_cond_wait(&cond, &lock);
    }

    T getTask()
    {
        T t =  task_queue_.front();
        task_queue_.pop();
        return t;
    }
private:
    // 懒汉模式，把构造私有化
    ThreadPool(int thread_num = g_thread_num)   // 类的构造
        :num_(thread_num)
    {                                           // 此处开始类的赋值
        pthread_mutex_init(&lock, nullptr);
        pthread_cond_init(&cond, nullptr);
        for (int i = 1; i <= num_; i ++)
        {
            threads_.push_back(new Thread(i, rountine, this));
        }
    }

    // 把拷贝构造封起来
    ThreadPool(const ThreadPool<T>& other) = delete;

    // 把赋值也封了
    const ThreadPool<T>& operator=(const ThreadPool<T>& other) = delete;
public:
    // 考虑一下多线程使用单例的过程
    static ThreadPool<T>* getThreadPool(int num = g_thread_num)
    {
        //  可以有效减少未来要进行加锁检测的问题
        // 拦截大量的在已经创建好单例的时候,剩余线程请求单例的而直接访问锁的行为
        if ( nullptr == thread_ptr)  // 双检查判断 --> 提升效率
        {
            lockGuard lockguard(&mutex);
            // 保证了线程安全,但是未来任何一个线程想获取单例,都必须调用getThreadPool接口
            // 但是会存在大量的申请和释放锁的行为   ——————  双检查判断 -> 提升效率
            if (nullptr == thread_ptr)
            {
                thread_ptr = new ThreadPool<T>(num);
            }
        }
        return thread_ptr;
    }

    // 1. run()
    void run()
    {
        for (auto& iter : threads_)
        {
            iter->start();
            //std::cout << iter->name() << "启动成功!" << std::endl;
            logMessage(NORMAL, "%s %s", iter->name().c_str(), "启动成功!");
        }
    }

    // 线程池本质也是一个生产消费模型

    // 有问题  线程的执行方法在类内？ 应该放在类外的，如果你要放在类内，用static
    static void* rountine(void* args)   // 消费过程  你要访问类内的成员，静态成员不在具有类内成员的属性
    {
        ThreadData* td = (ThreadData*)args;
        //task_queue.pop();   //无法访问类内的属性
        ThreadPool<T>* tp = (ThreadPool<T>*)td->args_;
        while (true)
        {
            T task;
            {   // 加锁部分,用个代码块 -> 临界区
                lockGuard lockguard(tp->getMutex()); // 执行方法无法访问私有成员，写一个成员函数即可
                while (tp->isEmpty())   tp->waitCond();

                // 读取任务
                task = tp->getTask(); // 任务队列是共享的 -> 将任务队列从共享的,拿到自己的私有空间
            }
            task(td->name_);
            //lock
            //while (task_queue.empty())  wait;
            //获取任务
            //unlock

            // 处理任务
            
        }
    }

    // 2. pushTask()
    void pushTask(const T& task)
    {
        lockGuard lockguard(&lock);
        task_queue_.push(task);
        pthread_cond_signal(&cond);
    }

    // // 临时接口
    // void joins()
    // {
    //     for (auto& iter : threads_)
    //     {
    //         iter->join();
    //     }
    // }

    ~ThreadPool()
    {
        for (auto& iter : threads_)
        {
            iter->join();
            delete iter;
        }
        pthread_mutex_destroy(&lock);
        pthread_cond_destroy(&cond);
    }
private:
    std::vector<Thread*> threads_;
    int num_;
    std::queue<T> task_queue_;

    static ThreadPool<T>* thread_ptr;
    static pthread_mutex_t mutex;

    // 方案二
    // queue1, queue2
    // std::queue<T>* p_queue, * c_queue
    // p_queue->queue1
    // c_queue->queue2
    // p_queue -> 生产一批任务之后，swap(p_queue, c_queue),环形所有线程
    // 当消费者处理完毕的时候，也可以进行swap(p_queue, c_queue)
    // 因为我们生产和消费用的是不同的对列，未来我们要进行资源处理的时候，仅仅是指针

    pthread_mutex_t lock;
    pthread_cond_t cond;
};

template<typename T>
ThreadPool<T>* ThreadPool<T>::thread_ptr = nullptr;

template<typename T>
pthread_mutex_t ThreadPool<T>::mutex = PTHREAD_MUTEX_INITIALIZER;
